C.S.Burner 🪙 ━►🔥GIT Node
Node / reticulum_mirror / MeshChatX / files / meshchatx / src / frontend / components / tools / RepositoryServerPage.vue
Displaying Raw • Download
meshchatx/src/frontend/components/tools/RepositoryServerPage.vue aeceb30d67ef2ff9ebf411a73daaab18438c5113 (aeceb30d) Text, 24.02 KB
T8b949e<!-- SPDX-License-Identifier: 0BSD -->
Tff7b72<templateTff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col flex-1 overflow-hidden min-w-0 bg-slate-50 dark:bg-zinc-950Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 overflow-y-auto w-full pb-[max(1rem,env(safe-area-inset-bottom))]Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-3 sm:p-4 md:p-6 max-w-6xl mx-auto w-full space-y-4 min-w-0Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap items-start justify-between gap-3 border-b border-gray-200 dark:border-zinc-800 pb-4Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start gap-3 min-w-0Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 bg-sky-100 dark:bg-sky-900/30 text-sky-600 dark:text-sky-300 rounded-lg shrink-0Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffpackage-variantTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-6Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmin-w-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-xs uppercase tracking-wide text-gray-500 dark:text-gray-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("tools.utilities") }}
Tff7b72</div>
Tff7b72<h1 Te6edf3class=Ta5d6ff"Ta5d6fftext-lg sm:text-xl font-bold text-gray-900 dark:text-white tracking-tightTa5d6ff"Tff7b72>
{{ Te6edf3$t("tools.repository_server.title") }}
Tff7b72</h1>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs sm:text-sm text-gray-600 dark:text-gray-400 mt-1 max-w-2xlTa5d6ff"Tff7b72>
{{ Te6edf3$t("tools.repository_server.subtitle") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 shrink-0Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-flex items-center justify-center p-2 rounded-lg border border-gray-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-gray-600 dark:text-zinc-300 hover:bg-gray-50 dark:hover:bg-zinc-800 transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6fftools.repository_server.refresh_bundled_tooltipTa5d6ff'Ta5d6ff)Ta5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffrefreshingTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffrefreshBundledTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffdownloadTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72<RouterLink
Te6edf3to=Ta5d6ff"Ta5d6ff/toolsTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-flex items-center gap-2 text-sm text-sky-600 dark:text-sky-300 hover:underlineTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffarrow-leftTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("tools.back_to_tools") }}
Tff7b72</RouterLink>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffrefreshingTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 px-3 py-2.5 space-y-1.5Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffh-1.5 bg-gray-200 dark:bg-zinc-700 rounded-full overflow-hiddenTa5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffh-full bg-sky-500 dark:bg-sky-400 rounded-full transition-[width] duration-300 ease-outTa5d6ff"
Te6edf3:style=Ta5d6ff"Ta5d6ff{ width: refreshProgressPct + Ta5d6ff'Ta5d6ff%Ta5d6ff'Ta5d6ff }Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<p Te6edf3v-if=Ta5d6ff"Ta5d6ffrefreshProgressLabelTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-600 dark:text-zinc-400 truncate font-monoTa5d6ff"Tff7b72>
{{ refreshProgressLabel }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-4 space-y-3Ta5d6ff"
Tff7b72>
Tff7b72<h2 Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-semibold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("tools.repository_server.http_heading") }}
Tff7b72</h2>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-3 items-endTa5d6ff"Tff7b72>
Tff7b72<label Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col gap-1 text-xs min-w-40Ta5d6ff"Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-gray-500 dark:text-zinc-500Ta5d6ff"Tff7b72>{{
Te6edf3$t("tools.repository_server.host_label")
}}Tff7b72</span>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffhttpHostTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-gray-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 px-2 py-1.5 text-sm text-gray-900 dark:text-whiteTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffhttpBusy || loading || httpRunningTa5d6ff"
Tff7b72/>
Tff7b72</label>
Tff7b72<label Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col gap-1 text-xs w-24Ta5d6ff"Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-gray-500 dark:text-zinc-500Ta5d6ff"Tff7b72>{{
Te6edf3$t("tools.repository_server.port_label")
}}Tff7b72</span>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffhttpPortTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3inputmode=Ta5d6ff"Ta5d6ffnumericTa5d6ff"
Te6edf3autocomplete=Ta5d6ff"Ta5d6ffoffTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-lg border border-gray-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 px-2 py-1.5 text-sm text-gray-900 dark:text-whiteTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffhttpBusy || loading || httpRunningTa5d6ff"
Tff7b72/>
Tff7b72</label>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-wrap gap-2Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-flex items-center gap-1.5 px-3 py-2 rounded-lg bg-emerald-600 text-white text-sm font-medium hover:bg-emerald-700 disabled:opacity-50 disabled:pointer-events-noneTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffhttpBusy || loading || httpRunningTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstartHttpTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffplayTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("tools.repository_server.start_http") }}
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-flex items-center gap-1.5 px-3 py-2 rounded-lg border border-gray-300 dark:border-zinc-600 bg-white dark:bg-zinc-900 text-gray-800 dark:text-zinc-200 text-sm font-medium hover:bg-gray-50 dark:hover:bg-zinc-800 disabled:opacity-50 disabled:pointer-events-noneTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffhttpBusy || loading || !httpRunningTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffstopHttpTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffstopTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("tools.repository_server.stop_http") }}
Tff7b72</button>
Tff7b72<button
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-flex items-center gap-1.5 px-3 py-2 rounded-lg border border-gray-300 dark:border-zinc-600 bg-white dark:bg-zinc-900 text-gray-800 dark:text-zinc-200 text-sm font-medium hover:bg-gray-50 dark:hover:bg-zinc-800 disabled:opacity-50 disabled:pointer-events-noneTa5d6ff"
Te6edf3:disabled=Ta5d6ff"Ta5d6ffhttpBusy || loadingTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffrestartHttpTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffrestartTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("tools.repository_server.restart_http") }}
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffhttpRunning && status?.http?.urlTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-xs space-y-1Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-gray-600 dark:text-zinc-400Ta5d6ff"Tff7b72>
{{ Te6edf3$t("tools.repository_server.http_listen_label") }}
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fffont-mono text-gray-900 dark:text-zinc-200Ta5d6ff"Tff7b72>{{ status.http.url }}Tff7b72</span>
Tff7b72</div>
Tff7b72<a
Te6edf3v-if=Ta5d6ff"Ta5d6ffbrowserRepoUrlTa5d6ff"
Te6edf3:href=Ta5d6ff"Ta5d6ffbrowserRepoUrlTa5d6ff"
Te6edf3target=Ta5d6ff"Ta5d6ff_blankTa5d6ff"
Te6edf3rel=Ta5d6ff"Ta5d6ffnoopener noreferrerTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffinline-flex items-center gap-1 text-sky-600 dark:text-sky-400 font-medium hover:underlineTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffopen-in-newTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("tools.repository_server.open_http") }}
Tff7b72</a>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 p-4 space-y-3Ta5d6ff"
Tff7b72>
Tff7b72<h2 Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-semibold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("tools.repository_server.upload_heading") }}
Tff7b72</h2>
Tff7b72<label
Te6edf3class=Ta5d6ff"Ta5d6ffinline-flex items-center gap-2 px-3 py-2 rounded-lg bg-sky-600 text-white text-sm font-medium cursor-pointer hover:bg-sky-700 transition-colorsTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffuploadTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4Ta5d6ff" Tff7b72/>
{{ Te6edf3$t("tools.repository_server.choose_file") }}
Tff7b72<input Te6edf3type=Ta5d6ff"Ta5d6fffileTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffhiddenTa5d6ff" @Te6edf3change=Ta5d6ff"Ta5d6ffonUploadTa5d6ff" Tff7b72/>
Tff7b72</label>
Tff7b72<p Te6edf3v-if=Ta5d6ff"Ta5d6fflastUploadErrorTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-red-600 dark:text-red-400Ta5d6ff"Tff7b72>{{ lastUploadError }}Tff7b72</p>
Tff7b72</div>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus?.last_refresh_failed && Object.keys(status.last_refresh_failed).lengthTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-amber-200 dark:border-amber-900/40 bg-amber-50 dark:bg-amber-950/30 p-3 text-xs text-amber-900 dark:text-amber-200Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fffont-semibold mb-1Ta5d6ff"Tff7b72>{{ Te6edf3$t("tools.repository_server.refresh_partial") }}Tff7b72</div>
Tff7b72<ul Te6edf3class=Ta5d6ff"Ta5d6fflist-disc pl-4 space-y-1Ta5d6ff"Tff7b72>
Tff7b72<li Te6edf3v-for=Ta5d6ff"Ta5d6ff(msg, pkg) in status.last_refresh_failedTa5d6ff" Te6edf3:key=Ta5d6ff"Ta5d6ffpkgTa5d6ff"Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fffont-monoTa5d6ff"Tff7b72>{{ pkg }}Tff7b72</span
>: {{ msg }}
Tff7b72</li>
Tff7b72</ul>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffrounded-xl border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 overflow-hiddenTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-3 border-b border-gray-100 dark:border-zinc-800 flex justify-between items-centerTa5d6ff"
Tff7b72>
Tff7b72<h2 Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-semibold text-gray-900 dark:text-whiteTa5d6ff"Tff7b72>
{{ Te6edf3$t("tools.repository_server.files_heading") }}
Tff7b72</h2>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500Ta5d6ff"Tff7b72>{{ entries.length }}Tff7b72</span>
Tff7b72</div>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffloadingTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffp-6 text-center text-sm text-gray-500Ta5d6ff"Tff7b72>
{{ Te6edf3$t("common.loading") }}
Tff7b72</div>
Tff7b72<div Te6edf3v-else-if=Ta5d6ff"Ta5d6ffentries.length === 0Ta5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffp-6 text-center text-sm text-gray-500Ta5d6ff"Tff7b72>
{{ Te6edf3$t("tools.repository_server.empty") }}
Tff7b72</div>
Tff7b72<table v-else Te6edf3class=Ta5d6ff"Ta5d6ffw-full text-left text-xsTa5d6ff"Tff7b72>
Tff7b72<thead Te6edf3class=Ta5d6ff"Ta5d6ffbg-gray-50 dark:bg-zinc-900/50 text-gray-500 uppercase tracking-wideTa5d6ff"Tff7b72>
Tff7b72<trTff7b72>
Tff7b72<th Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-2Ta5d6ff"Tff7b72>{{ Te6edf3$t("tools.repository_server.col_name") }}Tff7b72</th>
Tff7b72<th Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-2Ta5d6ff"Tff7b72>{{ Te6edf3$t("tools.repository_server.col_source") }}Tff7b72</th>
Tff7b72<th Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-2 text-rightTa5d6ff"Tff7b72>{{ Te6edf3$t("tools.repository_server.col_size") }}Tff7b72</th>
Tff7b72<th Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-2 w-24Ta5d6ff"Tff7b72>Tff7b72</th>
Tff7b72</tr>
Tff7b72</thead>
Tff7b72<tbody Te6edf3class=Ta5d6ff"Ta5d6ffdivide-y divide-gray-100 dark:divide-zinc-800 text-gray-800 dark:text-zinc-200Ta5d6ff"Tff7b72>
Tff7b72<tr Te6edf3v-for=Ta5d6ff"Ta5d6ffrow in entriesTa5d6ff" Te6edf3:key=Ta5d6ff"Ta5d6ffrow.name + row.sourceTa5d6ff"Tff7b72>
Tff7b72<td Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-2 font-mono break-allTa5d6ff"Tff7b72>{{ row.name }}Tff7b72</td>
Tff7b72<td Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-2Ta5d6ff"Tff7b72>{{ row.source }}Tff7b72</td>
Tff7b72<td Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-2 text-right tabular-numsTa5d6ff"Tff7b72>{{ formatBytes(row.bytes) }}Tff7b72</td>
Tff7b72<td Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-2 text-rightTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffrow.source === Ta5d6ff'Ta5d6ffuploadTa5d6ff'Ta5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-red-600 dark:text-red-400 hover:underline text-xs font-mediumTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffdeleteUpload(row.name)Ta5d6ff"
Tff7b72>
{{ Te6edf3$t("tools.repository_server.delete") }}
Tff7b72</button>
Tff7b72</td>
Tff7b72</tr>
Tff7b72</tbody>
Tff7b72</table>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72<scriptTff7b72>
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
import ToastUtils from "../../js/ToastUtils";
export default {
name: "RepositoryServerPage",
components: { MaterialDesignIcon },
data() {
return {
loading: true,
refreshing: false,
httpBusy: false,
httpHost: "127.0.0.1",
httpPort: "8787",
status: null,
entries: [],
lastUploadError: null,
refreshPollId: null,
};
},
computed: {
refreshProgressPct() {
const p = this.status?.refresh_progress;
if (!p || !p.total) {
return 0;
}
return Math.min(100, Math.round((100 * p.completed) / p.total));
},
refreshProgressLabel() {
const p = this.status?.refresh_progress;
if (!p?.current) {
return "";
}
return this.$t("tools.repository_server.refresh_fetching", { pkg: p.current });
},
httpRunning() {
return Boolean(this.status?.http?.running);
},
browserRepoUrl() {
const raw = this.status?.http?.url;
if (!raw) {
return null;
}
try {
const parsed = new URL(raw);
if (parsed.hostname === "0.0.0.0" && typeof window !== "undefined" && window.location?.hostname) {
parsed.hostname = window.location.hostname;
}
return parsed.toString();
} catch {
return raw;
}
},
},
async mounted() {
await this.loadAll();
},
beforeUnmount() {
this.stopRefreshPoll();
},
methods: {
stopRefreshPoll() {
if (this.refreshPollId != null) {
clearInterval(this.refreshPollId);
this.refreshPollId = null;
}
},
syncHttpFormFromStatus() {
const h = this.status?.http;
if (!h) {
return;
}
if (h.running) {
if (h.host) {
this.httpHost = h.host;
}
if (h.port != null) {
this.httpPort = String(h.port);
}
} else if (h.last_host) {
this.httpHost = h.last_host;
this.httpPort = h.last_port != null ? String(h.last_port) : "8787";
}
},
formatBytes(n) {
if (n Tff7b72< 1024) return `${n} B`;
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
},
httpErrorToast(errKey, detail) {
const Te6edf3map = Ta5d6ff{
invalid_host: this.$t("tools.repository_server.http_err_invalid_host"),
invalid_port: this.$t("tools.repository_server.http_err_invalid_port"),
already_running: this.$t("tools.repository_server.http_err_already_running"),
bind_failed: this.$t("tools.repository_server.http_err_bind_failed"),
};
let Te6edf3msg = Ta5d6ffmap[errKey] || this.$t("tools.repository_server.http_err_generic");
if (Te6edf3errKey =Ta5d6ff== "bind_failed" && detail) {
Te6edf3msg = Ta5d6ff`${msg}: ${detail}`;
} else if (!map[errKey] && detail) {
Te6edf3msg = Ta5d6ff`${msg}: ${detail}`;
}
ToastUtils.error(msg);
},
async loadAll() {
Te6edf3this.loading = Ta5d6fftrue;
Te6edf3this.lastUploadError = Ta5d6ffnull;
try {
const [s, list] = await Promise.all([
window.api.get("/api/v1/repository-server/status"),
window.api.get("/api/v1/repository-server/list"),
]);
Te6edf3this.status = Ta5d6ffs.data;
Te6edf3this.entries = Ta5d6ffArray.isArray(list.data) ? list.data : [];
this.syncHttpFormFromStatus();
} catch (e) {
ToastUtils.error(this.$t("tools.repository_server.load_failed"));
console.error(e);
} finally {
Te6edf3this.loading = Ta5d6fffalse;
}
},
buildHttpBody() {
const Te6edf3body = Ta5d6ff{};
const Te6edf3host = Ta5d6ff(this.httpHost || "").trim();
if (host) {
Te6edf3body.host = Ta5d6ffhost;
}
const Te6edf3portRaw = Ta5d6ff(this.httpPort || "").trim();
if (portRaw !== "") {
const Te6edf3p = Ta5d6ffparseInt(portRaw, 10);
if (!Number.isFinite(p)) {
return { error: "invalid_port" };
}
Te6edf3body.port = Ta5d6ffp;
}
return { body };
},
async startHttp() {
const Te6edf3built = Ta5d6ffthis.buildHttpBody();
if (Te6edf3built.error =Ta5d6ff== "invalid_port") {
this.httpErrorToast("invalid_port");
return;
}
Te6edf3this.httpBusy = Ta5d6fftrue;
try {
const { data } = await window.api.post("/api/v1/repository-server/http/start", built.body || {});
if (!data.ok) {
this.httpErrorToast(data.error, data.message);
} else {
ToastUtils.success(this.$t("tools.repository_server.http_started"));
}
await this.loadAll();
} catch (e) {
ToastUtils.error(this.$t("tools.repository_server.http_err_generic"));
console.error(e);
} finally {
Te6edf3this.httpBusy = Ta5d6fffalse;
}
},
async stopHttp() {
Te6edf3this.httpBusy = Ta5d6fftrue;
try {
await window.api.post("/api/v1/repository-server/http/stop");
ToastUtils.success(this.$t("tools.repository_server.http_stopped"));
await this.loadAll();
} catch (e) {
ToastUtils.error(this.$t("tools.repository_server.http_err_generic"));
console.error(e);
} finally {
Te6edf3this.httpBusy = Ta5d6fffalse;
}
},
async restartHttp() {
const Te6edf3built = Ta5d6ffthis.buildHttpBody();
if (Te6edf3built.error =Ta5d6ff== "invalid_port") {
this.httpErrorToast("invalid_port");
return;
}
Te6edf3this.httpBusy = Ta5d6fftrue;
try {
const { data } = await window.api.post("/api/v1/repository-server/http/restart", built.body || {});
if (!data.ok) {
this.httpErrorToast(data.error, data.message);
} else {
ToastUtils.success(this.$t("tools.repository_server.http_restarted"));
}
await this.loadAll();
} catch (e) {
ToastUtils.error(this.$t("tools.repository_server.http_err_generic"));
console.error(e);
} finally {
Te6edf3this.httpBusy = Ta5d6fffalse;
}
},
async refreshBundled() {
Te6edf3this.refreshing = Ta5d6fftrue;
ToastUtils.info(this.$t("tools.repository_server.refresh_started"));
this.stopRefreshPoll();
Te6edf3this.refreshPollId = Ta5d6ffsetInterval(() =Tff7b72> {
window.api
.get("/api/v1/repository-server/status")
.then(({ data }) => {
this.status = data;
this.syncHttpFormFromStatus();
})
.catch(() => {});
}, 350);
try {
const { data } = await window.api.post("/api/v1/repository-server/refresh-bundled");
if (data.error === "pip_not_found") {
ToastUtils.error(this.$t("tools.repository_server.refresh_pip_missing"));
await this.loadAll();
return;
}
const nFail = data.failed ? Object.keys(data.failed).length : 0;
const nOk = data.downloaded ? data.downloaded.length : 0;
if (nFail > 0 && nOk === 0) {
ToastUtils.error(this.$t("tools.repository_server.refresh_failed"));
} else if (nFail > 0) {
ToastUtils.warning(this.$t("tools.repository_server.refresh_partial_toast"));
ToastUtils.success(this.$t("tools.repository_server.refresh_ok_count", { n: nOk }));
} else {
ToastUtils.success(this.$t("tools.repository_server.refresh_ok"));
}
await this.loadAll();
} catch (e) {
ToastUtils.error(this.$t("tools.repository_server.refresh_failed"));
console.error(e);
await this.loadAll();
} finally {
this.stopRefreshPoll();
this.refreshing = false;
}
},
async onUpload(ev) {
const input = ev.target;
const file = input.files && input.files[0];
input.value = "";
if (!file) return;
this.lastUploadError = null;
const form = new FormData();
form.append("file", file, file.name);
try {
await window.api.post("/api/v1/repository-server/upload", form);
ToastUtils.success(this.$t("tools.repository_server.upload_ok"));
await this.loadAll();
} catch (e) {
this.lastUploadError =
e.response?.data?.error || e.message || this.$t("tools.repository_server.upload_failed");
ToastUtils.error(this.$t("tools.repository_server.upload_failed"));
}
},
async deleteUpload(name) {
if (!window.confirm(this.$t("tools.repository_server.delete_confirm", { name }))) {
return;
}
try {
const enc = encodeURIComponent(name);
await window.api.delete(`/api/v1/repository-server/upload/${enc}`);
ToastUtils.success(this.$t("tools.repository_server.delete_ok"));
await this.loadAll();
} catch (e) {
ToastUtils.error(this.$t("tools.repository_server.delete_failed"));
console.error(e);
}
},
},
};
Tff7b72</script>
Served by rngit 1.5.2 - Generated in 0.06s